package main

import (
	"fmt"
	"github.com/petermattis/goid"
	_ "math/rand"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	_ "testing"
	_ "time"
)

// main
//
//	@Description: 常见错误3：重入  【Mutex 不是可重入的锁！！！】
func main() {
	l := &sync.Mutex{}
	foo(l)

}

func foo(l sync.Locker) {
	fmt.Println("in foo")
	l.Lock()
	bar(l)
	l.Unlock()
}

func bar(l sync.Locker) {
	l.Lock()
	fmt.Println("in bar")
	l.Unlock()
}

// GoID
//
//	@Description: 使用runtime.Stack获取goroutine id
//	@return int
func GoID() int64 {
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	// 得到id字符串
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	id, err := strconv.Atoi(idField)
	if err != nil {
		panic(fmt.Sprintf("cannot get goroutine id: %v", err))
	}
	return int64(id)
}

// RecursiveMutex 实现可重入锁方案一：获取到 goroutine id，记录下获取锁的 goroutine id，它可以实现 Locker 接口。
// @Description: 自定义一个可重入锁结构体，重写
type RecursiveMutex struct {
	sync.Mutex
	owner     int64 // 当前持有锁的goroutine id
	recursion int32 // 这个goroutine 重入的次数
}

// Lock
//
//	@Description: 重写Locker的Lock方法
//	@receiver m
func (m *RecursiveMutex) Lock() {
	//使用第三方库获取goroutine id
	gid := goid.Get()
	// 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入
	if atomic.LoadInt64(&m.owner) == gid {
		atomic.AddInt32(&m.recursion, 1)
		return
	}
	m.Mutex.Lock()
	// 获得锁的goroutine第一次调用，记录下它的goroutine id,调用次数加1
	atomic.StoreInt64(&m.owner, gid)
	atomic.StoreInt32(&m.recursion, 1)

}

// Unlock
//
//	@Description: 重写Locker的Unlock方法
//	@receiver m
func (m *RecursiveMutex) Unlock() {
	//使用第三方库获取goroutine id
	gid := goid.Get()
	// 非持有锁的goroutine尝试释放锁，错误的使用
	if atomic.LoadInt64(&m.owner) != gid {
		panic(fmt.Sprintf("illegal Unlock() by non-owner goroutine %d (owner:%d)", gid, m.owner))
	}
	// 调用次数减1
	recursion := atomic.AddInt32(&m.recursion, -1)
	if recursion != 0 {
		return
	}
	// 此goroutine最后一次调用，需要释放锁
	atomic.StoreInt64(&m.owner, -1)
	m.Mutex.Unlock()
}

// TokenRecursiveMutex
// @Description:  实现可重入锁方案2。调用者自己提供一个 token，获取锁的时候把这个 token 传入，释放锁的时候也需要把这个 token 传入。
type TokenRecursiveMutex struct {
	sync.Mutex
	token     int64 // token
	recursion int64 // 这个goroutine 重入的次数
}

// Lock
//
//	@Description: 重写Locker接口的Lock方法
//	@receiver m
//	@param token
func (m *TokenRecursiveMutex) Lock(token int64) {
	if atomic.LoadInt64(&m.token) == token {
		//重入次数+1
		atomic.AddInt64(&m.recursion, 1)
		return
	}
	m.Mutex.Lock()
	atomic.StoreInt64(&m.token, token)
	atomic.StoreInt64(&m.recursion, 1)
}

// Unlock
//
//	@Description:  重写Locker接口的Unlock方法
//	@receiver m
//	@param token
func (m *TokenRecursiveMutex) Unlock(token int64) {
	if atomic.LoadInt64(&m.token) != token {
		panic(fmt.Sprintf("wrong the owner(%d): %d!", m.token, token))
	}
	// 调用次数减1
	recursion := atomic.AddInt64(&m.recursion, -1)
	if recursion != 0 {
		return
	}
	//释放锁
	atomic.StoreInt64(&m.token, 0)
	m.Mutex.Unlock()

}
